﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Cryptography;

namespace Dtf.Core.Remoting
{
    
    public class RemotingServer : RemotingObject
    {
        
        public RemotingServer()
        {

           // m_testCasesAppDomain = AppDomain.CreateDomain("TestCases");
            //string s = m_testCasesAppDomain.FriendlyName;
           // string name = AppDomain.CurrentDomain.FriendlyName; //CLEANUP
            //m_sponsor = AppDomain.CurrentDomain.CreateInstanceAndUnwrap("Dtf", typeof(Sponsor).FullName) as Sponsor;

          //  m_sponsor = new Sponsor();
            LogManager.Write("RemotingServer()");

            if (!Directory.Exists(Test_Case_Dir))
            {
                Directory.CreateDirectory(Test_Case_Dir);
            }
            
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AsmResolve); ;  //occur before assm to be loaded. use this to load test case dymatic
            //AppDomainSetup setup = new AppDomainSetup();
            ////setup.ApplicationBase = functionDirectory;
            //setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
            //setup.ApplicationName = "Graph";
            ////appDomain = AppDomain.CreateDomain("Functions", null, setup); 
            //m_testAppDomain = AppDomain.CreateDomain("T", null, setup);
            
        }

     

        public T CreateInstance<T>(params object[] args)
        {
           // AppDomain app = AppDomain.CreateDomain("c");

            //app.Load(System.IO.File.ReadAllBytes(@"e:\Longan\Dtf\GoaTest\bin\Debug\goatest.exe"));
            return (T)Activator.CreateInstance(typeof(T));
            
        
                //return (T)app.Load(System.IO.File.ReadAllBytes(@"c:\testcases\goatest.exe")).CreateInstance(typeof(T).FullName);
                //return (T)app.CreateInstanceFromAndUnwrap(@"c:\testcases\goatest.exe", typeof(T).FullName);
          /*  byte[] bs = System.IO.File.ReadAllBytes(@"c:\testcases\goatest.exe");
            //byte[] bs = System.IO.File.ReadAllBytes(@"E:\Longan\Dtf\c\bin\debug\c.exe");
            AppDomain app = AppDomain.CreateDomain("Crash");
            app.Load(bs);
            Assembly[] asms = app.GetAssemblies();
            
            foreach (Assembly asm in asms)
            {
                Console.WriteLine(asm.FullName);
                if (asm.FullName == typeof(T).Assembly.FullName)
                {
                    try
                    {
                        return (T)asm.CreateInstance(typeof(T).FullName);
                    }
                    catch (StackOverflowException)
                    {
                        return default(T);
                    }
                    catch (Exception)
                    {
                        return default(T);
                    }
                }
            }
            //Assembly asm = Assembly.LoadFile(@"c:\testcases\goatest.exe");
            

            app.CreateInstance(typeof(T).Assembly.FullName, typeof(T).FullName).Unwrap();
            //T t= (T)Assembly.GetAssembly(typeof(T)).CreateInstance(typeof(T).FullName, false, BindingFlags.Public | BindingFlags.Instance, null, args, null, null); //Run in separate appdomain. instead of "Activator.CreateInstance(typeof(T), args)" to avoid server crash if test case fail(crash).

            return default(T);// t;
           * 
           */
        }

        public void EndSession()
        {
            if (m_sponsor != null)
            {
                m_sponsor.DoRenew = false;
                m_sponsor = null;
            }
            
        }

        /// <summary>
        /// Copy test case
        /// </summary>
        /// <param name="relativePath"></param>
        /// <param name="fileData"></param>
        public void SyncFile(string relativePath, byte[] fileData)
        {
            string filePath = Path.Combine(Test_Case_Dir, relativePath);
            File.Delete(filePath);
            FileStream fs = File.OpenWrite(filePath);
            fs.Write(fileData, 0, fileData.Length);
            fs.Flush();
            fs.Close();
            LogManager.Write("Sync: " + relativePath);
        }

        /// <summary>
        /// compare file before copy for best performence.
        /// </summary>
        /// <param name="relativePath"></param>
        /// <param name="md5"></param>
        /// <returns></returns>
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        public bool CompareFileMD5(string relativePath, byte[] md5)
        {
            string filePath = Path.Combine(Test_Case_Dir, relativePath);
            if (!File.Exists(filePath))
            {
                return false;
            }
            MD5 fileMD5 = MD5.Create();
            FileStream fs = File.OpenRead(filePath);
            byte[] localMD5 = fileMD5.ComputeHash(fs);
            fs.Close();
            for (int i = 0; i < localMD5.Length; i++)
            {
                if (localMD5[i] != md5[i])
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// Invoke when assembly not found
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
        private Assembly AsmResolve(object sender, ResolveEventArgs args)
        {

            string loadName = args.Name;
            

            List<string> files = new List<string>();
            string[] strfiles = Directory.GetFiles(Test_Case_Dir, "*.dll", SearchOption.AllDirectories);
            files.AddRange(strfiles);
            strfiles = Directory.GetFiles(Test_Case_Dir, "*.exe", SearchOption.AllDirectories);
            files.AddRange(strfiles);
            foreach (string strfile in files)
            {

                string asmName;
               
                try
                {
                    asmName = AssemblyName.GetAssemblyName(strfile).FullName;
                    //asmCurName = this.GetType().Assembly.FullName;

                    
                    //m_asmTestCases = Assembly.Load(strfile);
                    //m_asmTestCases = Assembly.Load(File.ReadAllBytes(strfile)); //Load to memory to void file in use.
                }
                catch (Exception)
                {
                    //not a valid assembly
                    continue;
                }
                if (asmName == loadName)
                {
                    return Assembly.Load(File.ReadAllBytes(strfile));
                }
               
            }


            throw new FileNotFoundException("Test case is not foud with assembly name: " + loadName);

        }

        
        //private AppDomain m_testCasesAppDomain;
        private static Sponsor m_sponsor;

        public const string Test_Case_Dir = @"C:\TestCases"; //save test case here


    }
}
